﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

using Microsoft.Xrm.Sdk.Client;
using Microsoft.Xrm.Sdk.Query;
using Microsoft.Xrm.Sdk.Metadata;
using Microsoft.Xrm.Sdk.Messages;
using Microsoft.Xrm.Sdk;
using Microsoft.Crm.Sdk.Messages;
using Microsoft.Crm.Sdk.Samples;

using System.ServiceModel;
using Microsoft.Xrm.Sdk.Discovery;
using System.ServiceModel.Description;
using System.ServiceModel.Security;
using ICSharpCode.SharpZipLib.Zip;
using System.Xml;
using System.Windows.Controls;
using System.Xml.XPath;
using System.Text.RegularExpressions;
using Crm2011EventBinder.Async;
using System.Threading;
using System.Windows.Threading;
using System.Reflection;

using Crm2011EventBinder.Serialization;
using System.Xml.Linq;

//This file is part of Crm2011EventBinder.

//Crm2011EventBinder is free software: you can redistribute it and/or modify
//it under the terms of the GNU General Public License as published by
//the Free Software Foundation, either version 3 of the License, or
//(at your option) any later version.

//Crm2011EventBinder is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.

namespace Crm2011EventBinder
{

    public partial class MainWindow : Window
    {

        const string RegexFindJsFuncs = "(?<=(?:\\s|\\G|\\A))(?:var\\s(?<func>[\\w_]+)\\s*=\\s*function\\s*\\((?:\\s*|(?<vars>[\\s\\w_,]+))\\)|function\\s(?<func>[\\w_]+)\\s*\\((?:\\s*|(?<vars>[\\s\\w_,]+))\\)(?=(?:\\s|\\Z|\\.|\\?|\\!)))";

        #region Cross Thread Objects

            //  _SyncLock should be used while accessing any objects contain in this region
            object _SyncLock = new object();

            List<EntityContainer>               _EntityContainers = new List<EntityContainer>();                //Stores a collection of entity metadata needed for the entity combo box located in teh solution groupbox.
            List<OrganizationDetailContainer>   _OrganizationContainers = new List<OrganizationDetailContainer>();
            ServerConnection.Configuration      _Config = null;                                                 //Holds active connection configuration.  Used to create ServiceProxies
            OrganizationServiceProxy            _ServiceProxy;
            bool                                _InvalidLogin;                                                  //Holds whether or not a user was authenticated successfully. 
            Dictionary<String, byte[]>          _Files = new Dictionary<String, byte[]>();
            Guid                                _SolutionId;
            List<String>                        _Webresources = new List<String>();

        #endregion

        #region Delegates

        CrmAsyncHelper.dRetrieveEntityList _dRetrieveEntityListDelegate;
        CrmAsyncHelper.dRetrieveOrganizations _dRetrieveOrganizationsDelegate;
        CrmAsyncHelper.dWhoAmI _dWhoAmIDelegate;
        CrmAsyncHelper.dInitServiceProxy _dInitServiceProxyDelegate;
        CrmAsyncHelper.dCreateNewSolution _dCreateNewSolutionDelegate;
        CrmAsyncHelper.dSaveAndPublishSolution _dSaveAndPublishSolutionDelegate;
        CrmAsyncHelper.dDeleteSolution _dDeleteSolutionDelegate;
        CrmAsyncHelper.dGetWebResources _dGetWebResourcesDelegate;

        #endregion

        CrmAsyncHelper              _CrmAsync = new CrmAsyncHelper();
        Dictionary<String, String>  _EventStringMap = new Dictionary<String, String>();
        GifImage                    _LoadingGif;
        EntityMetadata              _SelectedEntity;                  //Holds entity metadata from entity combo box located in solution groupbox.
        String                      _SelectedEntityName;              //Holds entity logical name from entity combo box located in solution groupbox.
        String                      _SelectedEntityDisplayName;       //Holds entity display name from entity combo box located in solution groupbox.
        ServerConnection            _ServerConnect = new ServerConnection();
        
#region Refactored Methods/Events

        private void btnDeletePrevious_Click(object sender, RoutedEventArgs e)
        {

            lock (_SyncLock)
            {
                _Config = new ServerConnection.Configuration();
                _ServerConnect.DeleteConfigurationsFile();
                _ServerConnect = new ServerConnection();
                _ServiceProxy = null;
            }

            ClearForm();

            tvForms.Items.Clear();
            tvEvents.Items.Clear();
            tvFunction.Items.Clear();
            tvWebResources.Items.Clear();

            lblConnectionStatus.Text = "Disconnected";

            expanderSavedConfig.IsExpanded = true;
            expanderDisco.IsExpanded = false;
            expanderOrganization.IsExpanded = false;
            expanderCredentials.IsExpanded = false;
            expanderInfo.IsExpanded = false;

            lblInfoOrganization.Content = "";
            lblInfoServer.Content = "";
            lblInfoUsername.Content = "";

            cmbConnections.Items.Clear();
            cmbOrganizations.Items.Clear();
            cmbEntities.Items.Clear();

        }
        private void btnDiscover_Click(object sender, RoutedEventArgs e)
        {

            lock (_Config)
            {
                _Config = new ServerConnection.Configuration();
                _Config.ServerAddress = txtDiscover.Text;
                _Config.DiscoveryUri = ServerConnection.GetDiscoveryUri(txtDiscover.Text, chkSSL.IsChecked);
            }

            cmbOrganizations_Populate();

        }
        private void btnNewConnect_Click(object sender, RoutedEventArgs e)
        {
            expanderSavedConfig.IsExpanded = false;
            expanderDisco.IsExpanded = true;
            expanderOrganization.IsExpanded = false;
            expanderCredentials.IsExpanded = false;
        }
        private void btnRetrieve_Click(object sender, RoutedEventArgs e)
        {
            expanderSavedConfig.IsExpanded = false;
            expanderDisco.IsExpanded = false;
            expanderOrganization.IsExpanded = false;
            expanderCredentials.IsExpanded = false;
            expanderInfo.IsExpanded = false;

            cmbEntities_Populate();
        }
        private void btnSavConnection_Click(object sender, RoutedEventArgs e)
        {

            if (txtUsername.Text.Split('\\').Length != 2)
            {
                MessageBox.Show("Invalid uesr name. domain\\username is the correct format.");
                return;
            }

            lock (_SyncLock)
            {

                _Config.Credentials = new ClientCredentials();
                _Config.Credentials.Windows.ClientCredential = new System.Net.NetworkCredential(txtUsername.Text.Split('\\')[1], txtPassword.Password, txtUsername.Text.Split('\\')[0]);
                _Config.EndpointType = AuthenticationProviderType.ActiveDirectory;
                _Config.DeviceCredentials = ServerConnection.GetDeviceCredentials();
                _Config.OrganizationName = ((OrganizationDetailContainer)cmbOrganizations.SelectedValue).Organization.UniqueName;
                _Config.OrganizationUri = new Uri(((OrganizationDetailContainer)cmbOrganizations.SelectedValue).Organization.Endpoints[EndpointType.OrganizationService]);
                _ServiceProxy = ServerConnection.GetOrganizationProxy(_Config);
                _ServiceProxy.EnableProxyTypes();

            }

            ShowLoading();
            _dWhoAmIDelegate = _CrmAsync.WhoAmI;
            _dWhoAmIDelegate.BeginInvoke(_ServiceProxy, ref _InvalidLogin, new AsyncCallback(btnSavConnection_callback), null);

        }
        private void btnSavConnection_callback(IAsyncResult result)
        {

            lock (_SyncLock)
            {
                _dWhoAmIDelegate.EndInvoke(ref _InvalidLogin, result);
                _dWhoAmIDelegate = null;
            }

            btnSaveAndConnect.Dispatcher.BeginInvoke(new AsyncCallback(btnSavConnection_invoke), new object[] { result });
        }
        private void btnSavConnection_invoke(IAsyncResult result)
        {

            HideLoading();

            if (_InvalidLogin)
            {
                MessageBox.Show("Invalid login. Try again");
                return;
            }

            _ServerConnect.configurations.Add(_Config);
            _ServerConnect.SaveConfigurations();

            cmbConnections_Populate();
            cmbConnections.SelectedIndex = cmbConnections.Items.Count - 1;

            lblInfoServer.Content = "Server: " + _Config.ServerAddress.ToUpper();
            lblInfoOrganization.Content = "Organization: " + _Config.OrganizationName;
            lblInfoUsername.Content = "Username: " + _Config.Credentials.Windows.ClientCredential.Domain + "\\" + _Config.Credentials.Windows.ClientCredential.UserName;
            lblConnectionStatus.Text = "Connected";

            expanderSavedConfig.IsExpanded = false;
            expanderDisco.IsExpanded = false;
            expanderOrganization.IsExpanded = false;
            expanderCredentials.IsExpanded = false;
            expanderInfo.IsExpanded = true;

        }
        private void btnSaveAndPubish_invoke(IAsyncResult result)
        {
            ClearForm();

            tvEvents.Items.Clear();
            tvFunction.Items.Clear();
            tvWebResources.Items.Clear();

            HideLoading();
        }
        private void btnSaveAndPubish_callback(IAsyncResult result)
        {
            _dSaveAndPublishSolutionDelegate.EndInvoke(result);
            _dSaveAndPublishSolutionDelegate = null;
            btnSaveAndPubish.Dispatcher.BeginInvoke(new AsyncCallback(btnSaveAndPubish_invoke), new object[] { result });
        }
        private void btnSaveAndPubish_Click(object sender, RoutedEventArgs e)
        {
            ShowLoading();
            _Files["customizations.xml"] = UTF8Encoding.UTF8.GetBytes(FormXml.Doc.OuterXml);
            _dSaveAndPublishSolutionDelegate = _CrmAsync.SaveAndPublishSolution;
            _dSaveAndPublishSolutionDelegate.BeginInvoke(_ServiceProxy, _SelectedEntityName, _Files, btnSaveAndPubish_callback, null);
        }
        private void btnSelectSavedConfig_invoke(IAsyncResult result)
        {
            lblConnectionStatus.Text = "Connected";
            cmbEntities_Populate_invoke(result);
        }
        private void btnSelectSavedConfig_callback(IAsyncResult result) {

            _dInitServiceProxyDelegate.EndInvoke(ref _ServiceProxy, result);
            _dInitServiceProxyDelegate = null;

            btnSelectSavedConfig.Dispatcher.BeginInvoke(new AsyncCallback(btnSelectSavedConfig_invoke), new object[] { result });

        }
        private void btnSelectSavedConfig_Click(object sender, RoutedEventArgs e)
        {

            ShowLoading();

            lock (_SyncLock)
            {
                _Config = (ServerConnection.Configuration)cmbConnections.SelectedValue;
            }

            if (_Config == null)
                return;

            lblInfoServer.Content = "Server: " + _Config.ServerAddress.ToUpper();
            lblInfoOrganization.Content = "Organization: " + _Config.OrganizationName;
            lblInfoUsername.Content = "Username: " + _Config.Credentials.Windows.ClientCredential.Domain + "\\" + _Config.Credentials.Windows.ClientCredential.UserName;

            expanderSavedConfig.IsExpanded = false;
            expanderDisco.IsExpanded = false;
            expanderOrganization.IsExpanded = false;
            expanderCredentials.IsExpanded = false;
            expanderInfo.IsExpanded = false;

            _dInitServiceProxyDelegate = _CrmAsync.InitServiceProxy;
            _dInitServiceProxyDelegate.BeginInvoke(ref _ServiceProxy, _Config, _EntityContainers, btnSelectSavedConfig_callback, null);

        }
        private void btnSelectOrganization_Click(object sender, RoutedEventArgs e)
        {
            expanderSavedConfig.IsExpanded = false;
            expanderDisco.IsExpanded = false;
            expanderOrganization.IsExpanded = false;
            expanderCredentials.IsExpanded = true;
            expanderInfo.IsExpanded = false;
        }
        private void cmbConnections_Populate()
        {
            cmbConnections.Items.Clear();

            if (_ServerConnect.configurations.Count < 1)
                return;

            foreach (ServerConnection.Configuration x in _ServerConnect.configurations)
                cmbConnections.Items.Add(x);

            cmbConnections.SelectedIndex = 0;

        }
        private void cmbEntities_Populate()
        {
            ShowLoading();
            _dRetrieveEntityListDelegate = _CrmAsync.RetrieveEntityList;
            _dRetrieveEntityListDelegate.BeginInvoke(_ServiceProxy, _EntityContainers, cmbEntities_Populate_callback, null);
        }
        private void cmbEntities_Populate_callback(IAsyncResult result)
        {
            _dRetrieveEntityListDelegate.EndInvoke(result);
            _dRetrieveEntityListDelegate = null;
            //Call same method on main thread
            cmbEntities.Dispatcher.BeginInvoke(new AsyncCallback(cmbEntities_Populate_invoke), new object[] { result });

        }
        private void cmbEntities_Populate_invoke(IAsyncResult result)
        {
            //Create lock to prevent other threads from using _EntityContainers object.
            lock (_SyncLock)
            {

                cmbEntities.Items.Clear();
                foreach (EntityContainer x in _EntityContainers)
                    cmbEntities.Items.Add(x);

            }

            HideLoading();
            CollaspeLeft();

            dockRight.IsEnabled = true;
        }
        private void cmbEntities_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {

            EntityContainer ec = (EntityContainer)cmbEntities.SelectedValue;

            _SelectedEntity = null;
            _SelectedEntityName = "";
            _SelectedEntityDisplayName = "";

            if (ec == null)
                return;

            _SelectedEntityDisplayName = ec.EntityMetadata.DisplayName.UserLocalizedLabel.Label;
            _SelectedEntityName = ec.EntityMetadata.LogicalName;
            _SelectedEntity = ec.EntityMetadata;

        }
        private void cmbOrganizations_Populate()
        {
            ShowLoading();
            _dRetrieveOrganizationsDelegate = _CrmAsync.RetrieveOrganizations;
            _dRetrieveOrganizationsDelegate.BeginInvoke(_Config, _OrganizationContainers, cmbOrganizations_Populate_callback, null);
        }
        private void cmbOrganizations_Populate_callback(IAsyncResult result)
        {
            _dRetrieveOrganizationsDelegate.EndInvoke(result);
            _dRetrieveOrganizationsDelegate = null;
            //Call same method on main thread
            cmbOrganizations.Dispatcher.BeginInvoke(new AsyncCallback(cmbOrganizations_Populate_invoke), new object[] { result });
        }
        private void cmbOrganizations_Populate_invoke(IAsyncResult result)
        {
            //Create lock to prevent other threads from using _OrganizationContainers object.
            lock (_SyncLock)
            {

                cmbOrganizations.Items.Clear();
                foreach (OrganizationDetailContainer x in _OrganizationContainers)
                    cmbOrganizations.Items.Add(x);

            }

            HideLoading();

            if (cmbOrganizations.Items.Count < 1)
            {
                MessageBox.Show("Could not locate any CRM organizations.");
                return;
            }

            expanderSavedConfig.IsExpanded = false;
            expanderDisco.IsExpanded = false;
            expanderOrganization.IsExpanded = true;
            expanderCredentials.IsExpanded = false;

        }

#endregion

        public MainWindow()
        {
            InitializeComponent();

            _EventStringMap.Add("onload", "OnLoad");
            _EventStringMap.Add("onsave", "OnSave");
            _EventStringMap.Add("onchange", "OnChange");
            _EventStringMap.Add("tabstatechange", "TabStateChange");

            cmbConnections_Populate();

            try
            {
                



            }
            catch (FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault> ex)
            {
                Console.WriteLine("The application terminated with an error.");
                Console.WriteLine("Timestamp: {0}", ex.Detail.Timestamp);
                Console.WriteLine("Code: {0}", ex.Detail.ErrorCode);
                Console.WriteLine("Message: {0}", ex.Detail.Message);
                Console.WriteLine("Plugin Trace: {0}", ex.Detail.TraceText);
                Console.WriteLine("Inner Fault: {0}",
                    null == ex.Detail.InnerFault ? "Has Inner Fault" : "No Inner Fault");
            }
            catch (System.TimeoutException ex)
            {
                Console.WriteLine("The application terminated with an error.");
                Console.WriteLine("Message: {0}", ex.Message);
                Console.WriteLine("Stack Trace: {0}", ex.StackTrace);
                Console.WriteLine("Inner Fault: {0}",
                    null == ex.InnerException.Message ? "Has Inner Fault" : "No Inner Fault");
            }
            catch (System.Exception ex)
            {
                Console.WriteLine("The application terminated with an error.");
                Console.WriteLine(ex.Message);

                // Display the details of the inner exception.
                if (ex.InnerException != null)
                {
                    Console.WriteLine(ex.InnerException.Message);

                    FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault> fe = ex.InnerException
                        as FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault>;
                    if (fe != null)
                    {
                        Console.WriteLine("Timestamp: {0}", fe.Detail.Timestamp);
                        Console.WriteLine("Code: {0}", fe.Detail.ErrorCode);
                        Console.WriteLine("Message: {0}", fe.Detail.Message);
                        Console.WriteLine("Plugin Trace: {0}", fe.Detail.TraceText);
                        Console.WriteLine("Inner Fault: {0}",
                            null == fe.Detail.InnerFault ? "Has Inner Fault" : "No Inner Fault");
                    }
                }
            }
            // Additional exceptions to catch: SecurityTokenValidationException, ExpiredSecurityTokenException,
            // SecurityAccessDeniedException, MessageSecurityException, and SecurityNegotiationException.

            finally
            {
                Console.WriteLine("Press <Enter> to exit.");
                Console.ReadLine();
            }
        }

        private void CollaspeLeft()
        {
            expanderInfo.IsExpanded = false;
            expanderDisco.IsExpanded = false;
            expanderCredentials.IsExpanded = false;
            expanderOrganization.IsExpanded = false;
            expanderSavedConfig.IsExpanded = false;
        }
        private void HideLoading() 
        { 
            _LoadingGif.Visibility = Visibility.Hidden;
            dockLeft.IsEnabled = true;
            dockRight.IsEnabled = true;
        }
        private void ShowLoading() 
        { 
            _LoadingGif.Visibility = Visibility.Visible;
            dockLeft.IsEnabled = false;
            dockRight.IsEnabled = false;
        }
        
        private void Window_Loaded(object sender, RoutedEventArgs e)
        {


            _LoadingGif = new GifImage("Loading.gif");
            _LoadingGif.Visibility = Visibility.Hidden;
            
            stBar.Items.Add(_LoadingGif);

            lock (_SyncLock)
            {
                _Config = _ServerConnect.SetSavedConfigurations();
            }

            cmbConnections_Populate();
            
        }


        private void btnDownloadFormXml_invoke(IAsyncResult result)
        {

            if (!_Files.ContainsKey("customizations.xml"))
                return;

            FormXml = new FormXmlHelper();
            FormXml.Doc = new XmlDocument();
            FormXml.Doc.LoadXml(UTF8Encoding.UTF8.GetString(_Files["customizations.xml"]));

            PopulateFormsTree();
            HideLoading();

        }
        private void btnDownloadFormXml_callback(IAsyncResult result)
        {
            _dCreateNewSolutionDelegate.EndInvoke(ref _SolutionId, result);
            _dCreateNewSolutionDelegate = null;
            btnDownloadFormXml.Dispatcher.BeginInvoke(new AsyncCallback(btnDownloadFormXml_invoke), new object[] { result });
        }
        private void btnDownloadFormXml_Click(object sender, RoutedEventArgs e)
        {
            ShowLoading();
            _dCreateNewSolutionDelegate = _CrmAsync.CreateNewSolution;
            _dCreateNewSolutionDelegate.BeginInvoke(_ServiceProxy, ref _SolutionId,  _SelectedEntityName, _Files, btnDownloadFormXml_callback, null);
        }

        

        private void SetMainTabName()
        {
            tabForms.Header = EntityDisplayName + " Forms";
        }
        

        private FormXmlHelper FormXml;
        private XmlNode _XmlEntityNode;
        private XmlNode _XmlEntityNameNode;
        private String EntityLogicalName;
        private String EntityDisplayName;


        private void PopulateFormsTree()
        {


            _XmlEntityNode = FormXml.Doc.SelectSingleNode("//Entities/Entity");
            _XmlEntityNameNode = FormXml.Doc.SelectSingleNode("//Entities/Entity/Name");

            if (_XmlEntityNameNode == null)
            {
                MessageBox.Show("The selected entity does not a customization form.");
                return;
            }

            EntityDisplayName = _XmlEntityNameNode.Attributes["LocalizedName"].Value;
            EntityLogicalName = _XmlEntityNameNode.InnerText;

            //XmlNodeList Fields = _XmlEntityNode.SelectNodes("EntityInfo/entity/attributes/attribute");

            //TreeViewItem tviFields = new TreeViewItem();
            //tviFields.Header = "Fields (copy field by name double clicking fields)";
            //tviEntity.Items.Add(tviFields);

            //SortedDictionary<String, XmlNode> SortedFields = new SortedDictionary<string, XmlNode>();
            //String Name;
            //String DisplayName; 

            //foreach (XmlNode Field in Fields)
            //{
            //    DisplayName = Field.SelectSingleNode("displaynames/displayname[@languagecode='1033']").Attributes["description"].Value;
            //    Name = Field.SelectSingleNode("Name").InnerText;
            //    SortedFields.Add(DisplayName + " (" + Name + ")", Field);
            //}


            //TreeViewItem tviField;
            //foreach (KeyValuePair<String, XmlNode> Field in SortedFields)
            //{
            //    tviField = new TreeViewItem();
            //    tviField.Header = Field.Key;
            //    tviField.Tag = Field.Value;
            //    tviField.MouseDoubleClick += new MouseButtonEventHandler(OnFieldMouseDoubleClick);

            //    tviFields.Items.Add(tviField);
            //}

            SortedDictionary<String, XmlNode> SortedForms = new SortedDictionary<string, XmlNode>();

            XmlNodeList Forms = _XmlEntityNode.SelectNodes("FormXml/forms[@type='main']/systemform");
            XmlNodeList Tabs;
            XmlNodeList Sections;
            XmlNodeList Cells;

            String FormDisplayName;
            String FormId;

            TreeViewItem tviTab;
            TreeViewItem tviSection;
            TreeViewItem tviCell;

            foreach (XmlNode Form in Forms)
            {
                FormDisplayName = Form.SelectSingleNode("LocalizedNames/LocalizedName").Attributes["description"].Value;
                FormId = Form.SelectSingleNode("formid").InnerText;
                SortedForms.Add(FormDisplayName, Form);

            }

            TreeViewItem tviForm;
            TreeViewItem tviTabs;
            TreeViewItem tviSections;
            TreeViewItem tviCells;

            foreach (KeyValuePair<String, XmlNode> Form in SortedForms)
            {

                tviTabs = new TreeViewItem();
                tviTabs.Header = "Tabs";
                tviTabs.Tag = EventTypes.Tab;
                
                tviSections = new TreeViewItem();
                tviSections.Header = "Sections";
                tviSections.Tag = EventTypes.Section;

                tviCells = new TreeViewItem();
                tviCells.Header = "Cells";
                tviCells.Tag = EventTypes.Cell;

                tviForm = new TreeViewItem();
                tviForm.IsExpanded = true;
                tviForm.Header = Form.Key;
                tviForm.Tag = Form.Value;
                tviForm.Items.Add(tviTabs);
                tviForm.Items.Add(tviSections);
                tviForm.Items.Add(tviCells);
                
                tvForms.Items.Add(tviForm);

                SortedDictionary<String, XmlNode> SortedTabs = new SortedDictionary<string, XmlNode>();
                Tabs = Form.Value.SelectNodes("form/tabs/tab");

                string label;
                string controlid;
                XmlNode Control;
                foreach (XmlNode tab in Tabs) {

                    label = tab.SelectSingleNode("labels/label[@languagecode='1033']").Attributes["description"].Value;

                    if (String.IsNullOrEmpty(label.Trim()))
                        continue;

                    //Can't handle two tabs with the same name.
                    if (SortedTabs.ContainsKey(label))
                        continue;

                    SortedTabs.Add(label, tab);
                }

                foreach (KeyValuePair<String, XmlNode> tab in SortedTabs)
                {
                    tviTab = new TreeViewItem();
                    tviTab.Header = tab.Key;
                    tviTab.Tag = tab.Value;
                    tviTabs.Items.Add(tviTab);
                }

                SortedDictionary<String, XmlNode> SortedSections = new SortedDictionary<string, XmlNode>();
                Sections = Form.Value.SelectNodes("form/tabs/tab/columns/column/sections/section");

                foreach (XmlNode section in Sections)
                {

                    label = section.SelectSingleNode("labels/label[@languagecode='1033']").Attributes["description"].Value;

                    if (String.IsNullOrEmpty(label.Trim()))
                        continue;

                    SortedSections.Add(label, section);
                }

                foreach (KeyValuePair<String, XmlNode> section in SortedSections)
                {
                    tviSection = new TreeViewItem();
                    tviSection.Header = section.Key;
                    tviSection.Tag = section.Value;
                    tviSections.Items.Add(tviSection);
                }

                SortedDictionary<String, XmlNode> SortedCells = new SortedDictionary<string, XmlNode>();
                Cells = Form.Value.SelectNodes("form/tabs/tab/columns/column/sections/section/rows/row/cell");

                foreach (XmlNode cell in Cells)
                {

                    label = cell.SelectSingleNode("labels/label[@languagecode='1033']").Attributes["description"].Value;
                    Control = cell.SelectSingleNode("control");

                    if (Control == null)
                        continue;

                    if (String.IsNullOrEmpty(label.Trim()))
                        continue;

                    controlid = Control.Attributes["id"].Value;

                    SortedCells.Add(label + " (" + controlid + ")", cell);
                }
                
                
                foreach (KeyValuePair<String, XmlNode> cell in SortedCells)
                {

                    tviCell = new TreeViewItem();
                    tviCell.Header = cell.Key;
                    tviCell.Tag = cell.Value;
                    tviCells.Items.Add(tviCell);
                }
            }


            




            //ExpandRecursively(tvForms.Items, true);
            

        }

        private void PopulateWebResourcesTree_invoke(IAsyncResult result)
        {

            TreeViewItem tviAvailableWebresource;

            tvAvailableWebResources.Items.Clear();
            foreach (String s in _Webresources)
            {
                tviAvailableWebresource = new TreeViewItem();
                tviAvailableWebresource.Header = s;
                tvAvailableWebResources.Items.Add(tviAvailableWebresource);
            }

        }

        private void PopulateWebResourcesTree_callback(IAsyncResult result)
        {
            _dGetWebResourcesDelegate.EndInvoke(result);
            _dGetWebResourcesDelegate = null;
            tvAvailableWebResources.Dispatcher.BeginInvoke(new AsyncCallback(PopulateWebResourcesTree_invoke), new object[] { result });
        }


        private void PopulateWebResourcesTree()
        {

            tvWebResources.Items.Clear();

            if (SelectedForm == null)
                return;

            XmlNodeList WebResources = SelectedForm.SelectNodes("form/formLibraries/Library");

            TreeViewItem tviWebResource;
            
            foreach (XmlNode WebResource in WebResources)
            {
                tviWebResource = new TreeViewItem();
                tviWebResource.Header = WebResource.Attributes["name"].Value;
                tviWebResource.Tag = WebResource;
                tvWebResources.Items.Add(tviWebResource);
            }

            if (runOnce)
            {
                _dGetWebResourcesDelegate = _CrmAsync.GetWebResources;
                _dGetWebResourcesDelegate.BeginInvoke(_ServiceProxy, _Webresources, PopulateWebResourcesTree_callback, null);
                runOnce = false;
            }
        }

        bool runOnce = true;
        private static void ExpandRecursively(ItemCollection itemsControl, bool expand)
        {
            
            for (int i = itemsControl.Count - 1; i >= 0; --i)
            {
                TreeViewItem childControl = itemsControl[i] as TreeViewItem;

                if (childControl != null)
                {
                    ExpandRecursively(childControl.Items, expand);
                    childControl.IsExpanded = expand;
                }

                
            }

        }


        private void setTextXml() {


            using (StreamReader sr = new StreamReader("C:\\Users\\gilbertc\\documents\\visual studio 2010\\Projects\\Crm2011EventBinder\\Crm2011EventBinder\\data.xml"))
            {
                txtFormXml.Text = sr.ReadToEnd();
                sr.Close();
            }

            
            
            
        }

        private void OnFieldMouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            XmlNode Field = (XmlNode)((TreeViewItem)sender).Tag;
            Clipboard.SetText((Field.SelectSingleNode("LogicalName").InnerText));
        }

        private XmlNode SelectedForm;
        private XmlNode SelectedFormElement;
        private EventTypes SelectedEventType;

        private XmlNode FindRootNode(TreeViewItem Child)
        {
            if (Child == null)
                return null;

            if (Child.Parent is TreeView)
                return (XmlNode)Child.Tag;

            return FindRootNode((TreeViewItem) Child.Parent);
        }
        private EventTypes FindSelectedEventType(TreeViewItem SelectedItem)
        {

            if (SelectedItem == null)
                return EventTypes.None;

            if (SelectedItem.Parent is TreeView)
                return EventTypes.Form;

            TreeViewItem ParentItem = (TreeViewItem)SelectedItem.Parent;
            
            if(ParentItem.Tag is EventTypes)
                return (EventTypes) ParentItem.Tag;
            
            return EventTypes.None;
   
        }

        Dictionary<String, String> storedJS = new Dictionary<String, String>();

        private void tvForms_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
        {

            SetFormMode(FormModes.New);

            //Find parent element
            TreeViewItem SelectedItem = (TreeViewItem)e.NewValue;

            if (SelectedItem == null)
                return;

            SelectedForm = FindRootNode(SelectedItem);
            SelectedEventType = FindSelectedEventType(SelectedItem);

            PopulateWebResourcesTree();
            PopulateRegisteredEvents();

            if (SelectedEventType != EventTypes.None)
                SelectedFormElement = (XmlNode) SelectedItem.Tag;

        }

        private void DownloadJavascript(String webresourcename) {
            if (String.IsNullOrEmpty(webresourcename))
                return;

            if (storedJS.ContainsKey(webresourcename))
                storedJS.Remove(webresourcename);

            //Check to see if webresoruce exist.
            QueryExpression queryGetWebResource = new QueryExpression
            {
                EntityName = WebResource.EntityLogicalName,
                ColumnSet = new ColumnSet(new String[] { "content" }),
                Criteria = new FilterExpression()
            };

            queryGetWebResource.Criteria.AddCondition("name", ConditionOperator.Equal, webresourcename);

            var results = _ServiceProxy.RetrieveMultiple(queryGetWebResource);
            var all = results.Entities.ToList();

            if (results.Entities.Count < 1)
                return;

            if (results[0].GetAttributeValue<string>("content") == null)
                return;

            storedJS.Add(webresourcename, System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(results[0].GetAttributeValue<string>("content"))));


        }
        private void tvWebResources_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
        {

            TreeViewItem SelectedItem = (TreeViewItem)e.NewValue;

            if (SelectedItem == null)
                return;

            String webresourcename = (String)SelectedItem.Header;

            txtLibrary.Text = webresourcename;
            txtArgs.Text = "";

            if (storedJS.ContainsKey(webresourcename))
            {
                PopulateJavascriptFunctions(webresourcename);
                return;
            }

            DownloadJavascript(webresourcename);
            PopulateJavascriptFunctions(webresourcename);

        }

        private void SetFormMode(FormModes Mode)
        {
            FormMode = Mode;

            if (Mode == FormModes.New)
            {
                cmbEventType.IsEnabled = true;
                btnEventSaveEdit.Visibility = Visibility.Collapsed;
                btnEventCancelEdit.Visibility = Visibility.Collapsed;
                btnAddEvent.Visibility = Visibility.Visible;
            }
            else
            {
                cmbEventType.IsEnabled = false;
                btnEventSaveEdit.Visibility = Visibility.Visible;
                btnEventCancelEdit.Visibility = Visibility.Visible;
                btnAddEvent.Visibility = Visibility.Collapsed;
            }


        }
        private void tvEvents_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
        {

            TreeViewItem SelectedItem = (TreeViewItem)e.NewValue;

            if (SelectedItem == null)
                return;

            if (SelectedItem.Parent is TreeView)
                return;

            SetFormMode(FormModes.Edit);

            XmlNode EventNode = FindRootNode(SelectedItem);
            XmlNode HandlerNode = (XmlNode)SelectedItem.Tag;

            String EventName = EventNode.Attributes["name"].Value;

            txtLibrary.Text = HandlerNode.Attributes["libraryName"].Value;
            txtFunction.Text = HandlerNode.Attributes["functionName"].Value;

            if (HandlerNode.Attributes["enabled"] != null)
                chkEventEnabled.IsChecked = Boolean.Parse(HandlerNode.Attributes["enabled"].Value);
            else
                chkEventEnabled.IsChecked = false;

            if (HandlerNode.Attributes["passExecutionContext"] != null)
                chkEventPassContext.IsChecked = Boolean.Parse(HandlerNode.Attributes["passExecutionContext"].Value);
            else
                chkEventPassContext.IsChecked = false;

            cmbEventType.SelectedValue = _EventStringMap[EventName];

            if (HandlerNode.Attributes["parameters"] != null)
                txtArgs.Text = HandlerNode.Attributes["parameters"].Value;
            else
                txtArgs.Text = "";

        }
        private void PopulateRegisteredEvents()
        {

            tvEvents.Items.Clear();

            XmlNodeList Events = null;
            XmlNodeList EventHandlers = null;

            String functionName;
            String libraryName;

            if (SelectedEventType == EventTypes.None)
                return;
            
            switch(SelectedEventType)
            {
                case EventTypes.Form:

                    Events = SelectedForm.SelectNodes("form/events/event[not(@attribute) and not(@application='true')]");

                    break;
                case EventTypes.Tab:
                    
                    XmlNode Tab = (XmlNode)((TreeViewItem)tvForms.SelectedItem).Tag;
                    Events = Tab.SelectNodes("events/event");
                    break;

                case EventTypes.Section:

                    break;
                case EventTypes.Cell:

                    XmlNode Field = (XmlNode)((TreeViewItem)tvForms.SelectedItem).Tag;
                    String FieldName = Field.SelectSingleNode("control").Attributes["id"].Value;

                    Events = SelectedForm.SelectNodes("form/events/event[@attribute='" + FieldName + "' and not(@application='true')]");

                    break;
            }

            TreeViewItem tviEvent;
            TreeViewItem tviEventHandler;

            foreach (XmlNode Event in Events)
            {
                tviEvent = new TreeViewItem();
                tviEvent.Header = _EventStringMap[Event.Attributes["name"].Value];
                tviEvent.Tag = Event;
                tviEvent.IsExpanded = true;

                EventHandlers = Event.SelectNodes("Handlers/Handler");

                foreach (XmlNode EventHandler in EventHandlers)
                {

                    functionName = EventHandler.Attributes["functionName"].Value;
                    libraryName = EventHandler.Attributes["libraryName"].Value;

                    tviEventHandler = new TreeViewItem();
                    tviEventHandler.Header = functionName + " (" + libraryName + ")";
                    tviEventHandler.Tag = EventHandler;
                    tviEventHandler.IsExpanded = true;
                    tviEvent.Items.Add(tviEventHandler);
                }

                tvEvents.Items.Add(tviEvent);

            }

        }
        private void PopulateJavascriptFunctions(string webresource)
        {

            if (!storedJS.ContainsKey(webresource))
                return;

            tvFunction.Items.Clear();

            string SearchString = storedJS[webresource];

            MatchCollection jsFuncs = Regex.Matches(SearchString, RegexFindJsFuncs);



            List<String> SortedFuncs = new List<String>();

            TreeViewItem tviJsFunc;

            foreach (Match jsFunc in jsFuncs)
            {
                if (jsFunc.Groups["func"] == null)
                    continue;

                if (jsFunc.Groups["vars"] == null)
                    continue;

                SortedFuncs.Add(jsFunc.Groups["func"].Value + "(" + jsFunc.Groups["vars"].Value.Replace(" ", "").Replace(",",", ") + ")");

                
            }

            SortedFuncs.Sort();

            foreach (String x in SortedFuncs)
            {
                tviJsFunc = new TreeViewItem();
                tviJsFunc.Header = x;
                tvFunction.Items.Add(tviJsFunc);
            }


        }

        

        private void ClearForm()
        {
            txtFunction.Text = "";
            txtLibrary.Text = "";
            txtArgs.Text = "";
            chkEventEnabled.IsChecked = false;
            chkEventPassContext.IsChecked = false;
        }


        private void tvFunction_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
        {
            TreeViewItem SelectedItem = (TreeViewItem)e.NewValue;

            if (SelectedItem == null)
                return;

            String funcName = SelectedItem.Header.ToString();
            int funcNameEndPos = funcName.IndexOf("(");

            if(funcNameEndPos < 1)
                return;

            funcName = funcName.Substring(0, funcNameEndPos);

            txtFunction.Text = funcName;
            txtArgs.Text = "";
        }

        private enum FormModes
        {
            New = 0,
            Edit = 1 
        }

        private FormModes FormMode;

        private void btnConfirmChange_Click(object sender, RoutedEventArgs e)
        {

        }

        private void btnAddEvent_Click(object sender, RoutedEventArgs e)
        {
            //Depending on the event type (form, cell, tab) we need to change the XPath to retrieve the event registration location.

            if (txtFunction.Text == "")
            {
                MessageBox.Show("You must specify a function name to register.");
                return;
            }
            if (txtLibrary.Text == "")
            {
                MessageBox.Show("You must specify a library to register to.");
                return;
            }
            if (SelectedHandlerType == null)
            {
                MessageBox.Show("You must select an event type to register to.");
                return;
            }
                
            String EventXPath = "";
            XmlNode EventParentNode = null;

            

            if (SelectedEventType == EventTypes.Form)
            {
                EventXPath = "form/events";
                EventParentNode = SelectedForm.SelectSingleNode("form");
            }
            if (SelectedEventType == EventTypes.Tab)
            {
                EventXPath = "form/tabs/tab/events";
                EventParentNode = SelectedFormElement;
            }
            if (SelectedEventType == EventTypes.Cell) //Form and cells are registered in the same location
            { 
                EventXPath = "form/events";
                EventParentNode = SelectedForm.SelectSingleNode("form");
            }

            if (EventParentNode == null)
                return;

            XmlNode EventsNode = SelectedForm.SelectSingleNode(EventXPath);
            XmlNode EventNode;
            
            if(SelectedEventType == EventTypes.Cell)
                EventNode = GetEventNodeWithAttr(EventParentNode, EventsNode, SelectedFormElement);
            else
                EventNode = GetEventNode(EventParentNode, EventsNode, SelectedFormElement);


            Handler NewHandler = new Handler();
            NewHandler.functionName = txtFunction.Text.Trim();
            NewHandler.libraryName = txtLibrary.Text.Trim();
            NewHandler.parameters = txtArgs.Text.Trim();
            NewHandler.handlerUniqueId = Guid.NewGuid().ToString("B");  //B, bracets {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
            NewHandler.passExecutionContext = (bool)chkEventPassContext.IsChecked;
            NewHandler.enabled = (bool)chkEventEnabled.IsChecked;

            XmlNode HandlersNode = EventNode.SelectSingleNode("Handlers");
            HandlersNode.InnerXml = HandlersNode.InnerXml + NewHandler.ToString();

            PopulateRegisteredEvents();

            txtFunction.Text = "";
            txtLibrary.Text = "";
            chkEventEnabled.IsChecked = true;
            chkEventPassContext.IsChecked = false;
            cmbEventType.SelectedIndex = -1;
            
        }

        private String SelectedHandlerType;
        private void cmbEventType_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {

            if (cmbEventType.SelectedItem == null)
            {
                SelectedHandlerType = null;
                return;
            }


            SelectedHandlerType = ((String)((ComboBoxItem)cmbEventType.SelectedItem).Content).ToLower();
        }

        private XmlNode GetEventNodeWithAttr(XmlNode EventParentNode, XmlNode EventsNode, XmlNode SelectedFormElement) {

            if(SelectedEventType != EventTypes.Cell)
                return null;

            String Attribute = SelectedFormElement.SelectSingleNode("control").Attributes["id"].Value;

            //If the EventNode is null, then we must create the events node.
            if (EventsNode == null)
            {
                Events NewEventNode = new Events();
                NewEventNode.Active = true;
                NewEventNode.Application = false; //Not sure what this does.
                NewEventNode.Name = SelectedHandlerType;  //Handler type is set by cmbEventType...(onload, onsave, onchange...)
                NewEventNode.Attribute = Attribute;
                EventsNode = FormXml.Doc.CreateNode(XmlNodeType.Element, "events", null);
                EventsNode.InnerXml = NewEventNode.ToString();
                EventParentNode.AppendChild(EventsNode);
            }

            XmlNode EventNode = EventsNode.SelectSingleNode("event[@name='" + SelectedHandlerType + "' and @attribute='" + Attribute + "' and not(@application='true')]");

            if (EventNode == null)
            {
                Events NewEventNode = new Events();
                NewEventNode.Active = true;
                NewEventNode.Application = false; //Not sure what this does.
                NewEventNode.Name = SelectedHandlerType;  //Handler type is set by cmbEventType...(onload, onsave, onchange...)
                NewEventNode.Attribute = Attribute;
                //Append Events node with new event node.
                EventsNode.InnerXml = EventsNode.InnerXml + NewEventNode.InnerXml();
            }

            return EventsNode.SelectSingleNode("event[@name='" + SelectedHandlerType + "' and @attribute='" + Attribute + "' and not(@application='true')]");

        }
        private XmlNode GetEventNode(XmlNode EventParentNode, XmlNode EventsNode, XmlNode SelectedFormElement)
        {

            //If the EventNode is null, then we must create the events node.
            if (EventsNode == null)
            {
                Events NewEventNode = new Events();
                NewEventNode.Active = true;
                NewEventNode.Application = false; //Not sure what this does.
                NewEventNode.Name = SelectedHandlerType;  //Handler type is set by cmbEventType...(onload, onsave, onchange...)
                EventsNode = FormXml.Doc.CreateNode(XmlNodeType.Element, "events", null);
                EventsNode.InnerXml = NewEventNode.InnerXml();
                EventParentNode.AppendChild(EventsNode);
            }

            XmlNode EventNode = EventsNode.SelectSingleNode("event[@name='" + SelectedHandlerType + "' and not(@application='true')]");
            
            if (EventNode == null)
            {
                Events NewEventNode = new Events();
                NewEventNode.Active = true;
                NewEventNode.Application = false; //Application = false tells CRM that this is not a system event handler.
                NewEventNode.Name = SelectedHandlerType;  //Handler type is set by cmbEventType...(onload, onsave, onchange...)
                //Append Events node with new event node.
                EventsNode.InnerXml = EventsNode.InnerXml + NewEventNode.InnerXml();
            }

            return EventsNode.SelectSingleNode("event[@name='" + SelectedHandlerType + "' and not(@application='true')]");

        }

        private void btnDeleteRegisteredEvent_Click(object sender, RoutedEventArgs e)
        {

            TreeViewItem SelectedItem = (TreeViewItem)tvEvents.SelectedItem;

            if (SelectedItem == null)
                return;

            if (SelectedItem.Parent is TreeView)
                return;

            XmlNode SelectedNode = (XmlNode)SelectedItem.Tag;

            if (SelectedNode.ParentNode.ChildNodes.Count == 1)
            {
                XmlNode EventNode = SelectedNode.ParentNode.ParentNode;

                if (EventNode.ParentNode.ChildNodes.Count > 1)
                {
                    EventNode.ParentNode.RemoveChild(EventNode);
                }
                else
                {
                    XmlNode EventsNode = EventNode.ParentNode;
                    EventsNode.ParentNode.RemoveChild(EventsNode);
                }
                
            }
            else
            {
                SelectedNode.ParentNode.RemoveChild(SelectedNode);
            }

            SelectedNode = null;
            
            PopulateRegisteredEvents();

            SetFormMode(FormModes.New);
            ClearForm();
        }


        private void btnDeleteSolution_invoke(IAsyncResult result)
        {
            
            cmbEntities.SelectedIndex = -1;

            ClearForm();

            tvForms.Items.Clear();
            tvEvents.Items.Clear();
            tvFunction.Items.Clear();
            tvWebResources.Items.Clear();

            HideLoading();
        }
        private void btnDeleteSolution_callback(IAsyncResult result)
        {
            _dDeleteSolutionDelegate.EndInvoke(result);
            _dDeleteSolutionDelegate = null;

            btnDeleteSolution.Dispatcher.BeginInvoke(new AsyncCallback(btnDeleteSolution_invoke), new object[] { result });

        }
        private void btnDeleteSolution_Click(object sender, RoutedEventArgs e)
        {
            ShowLoading();
            _dDeleteSolutionDelegate = _CrmAsync.DeleteSolution;
            _dDeleteSolutionDelegate.BeginInvoke(_ServiceProxy, _SolutionId, btnDeleteSolution_callback, null);
        }

        private void tvWebResources_MouseUp(object sender, MouseButtonEventArgs e)
        {
            TreeViewItem SelectedItem = (TreeViewItem)tvWebResources.SelectedItem;

            if (SelectedItem == null)
                return;

            String webresourcename = (String)SelectedItem.Header;

            txtLibrary.Text = webresourcename;
        }

        private void tvFunction_MouseUp(object sender, MouseButtonEventArgs e)
        {
            TreeViewItem SelectedItem = (TreeViewItem)tvFunction.SelectedItem;

            if (SelectedItem == null)
                return;

            String funcName = SelectedItem.Header.ToString();
            int funcNameEndPos = funcName.IndexOf("(");

            if (funcNameEndPos < 1)
                return;

            funcName = funcName.Substring(0, funcNameEndPos);

            txtFunction.Text = funcName;
            txtArgs.Text = "";
        }

        private void btnEventCancelEdit_Click(object sender, RoutedEventArgs e)
        {
            ClearForm();
            SetFormMode(FormModes.New);
        }

        private void btnEventSaveEdit_Click(object sender, RoutedEventArgs e)
        {

            TreeViewItem SelectedItem = (TreeViewItem) tvEvents.SelectedItem;

            if (SelectedItem == null)
                return;

            XmlNode HandlerNode = (XmlNode)SelectedItem.Tag;


            if(HandlerNode.Attributes["libraryName"] == null) HandlerNode.Attributes.Append(FormXml.Doc.CreateAttribute("libraryName"));
            if (HandlerNode.Attributes["functionName"] == null) HandlerNode.Attributes.Append(FormXml.Doc.CreateAttribute("functionName"));
            if (HandlerNode.Attributes["enabled"] == null) HandlerNode.Attributes.Append(FormXml.Doc.CreateAttribute("enabled"));
            if (HandlerNode.Attributes["passExecutionContext"] == null) HandlerNode.Attributes.Append(FormXml.Doc.CreateAttribute("passExecutionContext"));
            if (HandlerNode.Attributes["parameters"] == null) HandlerNode.Attributes.Append(FormXml.Doc.CreateAttribute("parameters"));

            HandlerNode.Attributes["libraryName"].Value = txtLibrary.Text.Trim();
            HandlerNode.Attributes["functionName"].Value = txtFunction.Text.Trim();
            HandlerNode.Attributes["enabled"].Value = chkEventEnabled.IsChecked.Value.ToString().ToLower();
            HandlerNode.Attributes["passExecutionContext"].Value = chkEventPassContext.IsChecked.Value.ToString().ToLower();
            HandlerNode.Attributes["parameters"].Value = txtArgs.Text.Trim();

            SetFormMode(FormModes.New);

            PopulateRegisteredEvents();

            ClearForm();

        }

        private void tvEvents_MouseUp(object sender, MouseButtonEventArgs e)
        {
            TreeViewItem SelectedItem = (TreeViewItem)tvEvents.SelectedItem;

            if (SelectedItem == null)
                return;

            if (SelectedItem.Parent is TreeView)
                return;

            SetFormMode(FormModes.Edit);

            XmlNode EventNode = FindRootNode(SelectedItem);
            XmlNode HandlerNode = (XmlNode)SelectedItem.Tag;

            String EventName = EventNode.Attributes["name"].Value;

            txtLibrary.Text = HandlerNode.Attributes["libraryName"].Value;
            txtFunction.Text = HandlerNode.Attributes["functionName"].Value;

            if (HandlerNode.Attributes["enabled"] != null)
                chkEventEnabled.IsChecked = Boolean.Parse(HandlerNode.Attributes["enabled"].Value);
            else
                chkEventEnabled.IsChecked = false;

            if (HandlerNode.Attributes["passExecutionContext"] != null)
                chkEventPassContext.IsChecked = Boolean.Parse(HandlerNode.Attributes["passExecutionContext"].Value);
            else
                chkEventPassContext.IsChecked = false;

            cmbEventType.SelectedValue = _EventStringMap[EventName];

            if (HandlerNode.Attributes["parameters"] != null)
                txtArgs.Text = HandlerNode.Attributes["parameters"].Value;
            else
                txtArgs.Text = "";
        }

        private void btnResourceRemoved_Click(object sender, RoutedEventArgs e)
        {

            TreeViewItem SelectedItem = (TreeViewItem)tvWebResources.SelectedItem;

            if (SelectedItem == null)
                return;

            XmlNode WebResource = (XmlNode)SelectedItem.Tag;

            String webresourcename = (String)SelectedItem.Header;
            
            foreach(XmlNode Handler in SelectedForm.SelectNodes("//Handler[@libraryName='" + webresourcename + "']"))
            {
                //Check to see if the handler is the last handler in the list of events. If it is, remove the event entirely.
                if(Handler.ParentNode.ChildNodes.Count > 1)
                {
                    Handler.ParentNode.RemoveChild(Handler);
                } else {

                    //Remove event handler because it contains no handlers
                    XmlNode Event = Handler.ParentNode.ParentNode;
                    
                    if (Event.ParentNode.ChildNodes.Count > 1)
                    {
                        Event.ParentNode.RemoveChild(Event);
                    }
                    else
                    {
                        //Delete events node if Event is the last child.
                        XmlNode EventsNode = Event.ParentNode;
                        EventsNode.ParentNode.RemoveChild(EventsNode);
                    }
                    
                }

            }


            //Check to see if any form libraries are associated to the form.
            if (WebResource.ParentNode.ChildNodes.Count > 1)
            {
                WebResource.ParentNode.RemoveChild(WebResource);
            }
            else
            {
                XmlNode formLibrary = WebResource.ParentNode;
                formLibrary.ParentNode.RemoveChild(formLibrary);
            }
            

            PopulateWebResourcesTree();
            PopulateRegisteredEvents();

            tvFunction.Items.Clear();

        }

        private void btnLinkResource_Click(object sender, RoutedEventArgs e)
        {
            TreeViewItem SelectedItem = (TreeViewItem)tvAvailableWebResources.SelectedItem;

            if (SelectedItem == null)
                return;
            
            if (SelectedForm == null)
                return;

            String webresourcename = (String)SelectedItem.Header;

            XmlNode FormLibraries = SelectedForm.SelectSingleNode("form/formLibraries");

            if (FormLibraries == null)
            {
                FormLibraries = FormXml.Doc.CreateElement("formLibraries");
                XmlNode Form = SelectedForm.SelectSingleNode("form");
                Form.AppendChild(FormLibraries);
            }

            //<Library name="Account_main_library.js" libraryUniqueId="{44627fe8-b3dd-49f5-a2da-c05ea9acd4c9}" />
            XmlElement Library = FormXml.Doc.CreateElement("Library");
            XmlAttribute name = FormXml.Doc.CreateAttribute("name");
            name.Value = webresourcename;
            XmlAttribute libraryUniqueId = FormXml.Doc.CreateAttribute("libraryUniqueId");
            libraryUniqueId.Value = Guid.NewGuid().ToString("b");

            Library.Attributes.Append(name);
            Library.Attributes.Append(libraryUniqueId);

            FormLibraries.AppendChild(Library);

            PopulateWebResourcesTree();
        }
        private void btnEventMoveUp_Click(object sender, RoutedEventArgs e)
        {

            TreeViewItem SelectedItem = (TreeViewItem)tvEvents.SelectedItem;

            if (SelectedItem == null)
                return;

            XmlNode SelectedNode = (XmlNode)SelectedItem.Tag;

            XmlNode PreviousNode = SelectedNode.PreviousSibling;

            if (PreviousNode == null)
                return;

            XmlNode SelectedCopy = SelectedNode.Clone();
            XmlNode PreviousCopy = PreviousNode.Clone();

            SelectedNode.ParentNode.ReplaceChild(PreviousCopy, SelectedNode);
            PreviousNode.ParentNode.ReplaceChild(SelectedCopy, PreviousNode);

            PopulateRegisteredEvents();

        }
        private void btnEventMoveDown_Click(object sender, RoutedEventArgs e)
        {
            TreeViewItem SelectedItem = (TreeViewItem)tvEvents.SelectedItem;

            if (SelectedItem == null)
                return;

            XmlNode SelectedNode = (XmlNode)SelectedItem.Tag;

            XmlNode NextNode = SelectedNode.NextSibling;

            if (NextNode == null)
                return;

            XmlNode SelectedCopy = SelectedNode.Clone();
            XmlNode NextCopy = NextNode.Clone();

            SelectedNode.ParentNode.ReplaceChild(NextCopy, SelectedNode);
            NextNode.ParentNode.ReplaceChild(SelectedCopy, NextNode);

            PopulateRegisteredEvents();
        } 
        private void btnWebResourceMoveUp_Click(object sender, RoutedEventArgs e)
        {

            TreeViewItem SelectedItem = (TreeViewItem)tvWebResources.SelectedItem;

            if (SelectedItem == null)
                return;

            XmlNode SelectedNode = (XmlNode)SelectedItem.Tag;

            XmlNode PreviousNode = SelectedNode.PreviousSibling;

            if (PreviousNode == null)
                return;

            XmlNode SelectedCopy = SelectedNode.Clone();
            XmlNode PreviousCopy = PreviousNode.Clone();

            SelectedNode.ParentNode.ReplaceChild(PreviousCopy, SelectedNode);
            PreviousNode.ParentNode.ReplaceChild(SelectedCopy, PreviousNode);

            PopulateWebResourcesTree();
            
        }
        private void btnWebResourceMoveDown_Click(object sender, RoutedEventArgs e)
        {
            TreeViewItem SelectedItem = (TreeViewItem)tvWebResources.SelectedItem;

            if (SelectedItem == null)
                return;

            XmlNode SelectedNode = (XmlNode)SelectedItem.Tag;

            XmlNode NextNode = SelectedNode.NextSibling;

            if (NextNode == null)
                return;

            XmlNode SelectedCopy = SelectedNode.Clone();
            XmlNode NextCopy = NextNode.Clone();

            SelectedNode.ParentNode.ReplaceChild(NextCopy, SelectedNode);
            NextNode.ParentNode.ReplaceChild(SelectedCopy, NextNode);

            PopulateWebResourcesTree();
        }

        private void btnRefresh_Click(object sender, RoutedEventArgs e)
        {
            TreeViewItem SelectedItem = (TreeViewItem)tvWebResources.SelectedItem;

            if (SelectedItem == null)
                return;

            String webresourcename = (String)SelectedItem.Header;

            DownloadJavascript(webresourcename);
            PopulateJavascriptFunctions(webresourcename);
        }

        private void btnRefreshResources_Click(object sender, RoutedEventArgs e)
        {
            _dGetWebResourcesDelegate = _CrmAsync.GetWebResources;
            _dGetWebResourcesDelegate.BeginInvoke(_ServiceProxy, _Webresources, PopulateWebResourcesTree_callback, null);
        } 


    }

    public enum EventTypes
    {
        None = -1,
        Form = 0,
        Tab = 1,
        Section = 2,
        Cell = 3
    }

    public class FormXmlHelper
    {

        public XmlDocument Doc { get; set; }
        public XmlNode Form { get; set; }
        public XmlNode SystemForm { get; set; }
        
        /// <summary>
        /// XPath : //ImportExportXml/Entities/Entity/FormXml/forms/events
        /// Location for storing OnSave, OnLoad and OnChange events.
        /// </summary>
        public XmlNode FormEvents { get; set; }
        
        /// <summary>
        /// XPath : //ImportExportXml/Entities/Entity/FormXml/forms/formLibraries
        /// Location for storing OnSave, OnLoad and OnChange events.
        /// </summary>
        public XmlNode FormLibraries { get; set; }

    }

}

